package cn.stimd.spring.beans;

import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.ConverterNotFoundException;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import java.beans.PropertyEditor;
import java.lang.reflect.Array;
import java.lang.reflect.Field;

/**
 * Created by Stimd
 * 项目地址：https://gitee.com/stimd/spring-wheel
 * 公众号：Java编程探微
 */
public abstract class TypeConverterSupport extends PropertyEditorRegistrySupport implements TypeConverter {
    ConversionService conversionService;

    @Override
    public <T> T convertIfNecessary(Object value, Class<T> requiredType) throws TypeMismatchException{
        return convertIfNecessary(value, requiredType, TypeDescriptor.valueOf(requiredType) );
    }

    @Override
    public <T> T convertIfNecessary(Object value, Class<T> requiredType, MethodParameter methodParam) throws TypeMismatchException{
        return convertIfNecessary(value, requiredType, new TypeDescriptor(methodParam));
    }

    @Override
    public <T> T convertIfNecessary(Object value, Class<T> requiredType, Field field) throws TypeMismatchException {
        return convertIfNecessary( value, requiredType, new TypeDescriptor(field));
    }

    /**
     * 使用ConversionService和PropertyEditor进行类型转换，涵盖绝大多数场景
     * @param newValue          将要设置的值
     * @param requiredType      目标类型
     * @param typeDescriptor    目标类型描述符，可以是Class、Method、Field
     */
    public <T> T convertIfNecessary(Object newValue, Class<T> requiredType, TypeDescriptor typeDescriptor) throws TypeMismatchException {
        try {
            //1. ConversionService转换
            PropertyEditor editor = findCustomEditor(requiredType);
            if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
                TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
                if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
                    return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
                }
            }

            //2. PropertyEditor转换
            if(editor == null){
                editor = getDefaultEditor(requiredType);
            }

            if(editor!= null){
                //待设置的值如果是一个String[]，转成逗号拼接的字符串
                if (requiredType != null && !requiredType.isArray() && newValue instanceof String[]) {
                    newValue = StringUtils.arrayToCommaDelimitedString((String[]) newValue);
                }

                if(newValue instanceof String){
                    editor.setAsText((String) newValue);
                }else{
                    editor.setValue(newValue);
                }
                return (T) editor.getValue();
            }

            //3. 特殊的类型转换
            Object convertedValue = newValue;
            if(requiredType != null && convertedValue != null){
                //数组转数组，比如String[]转Class[]
                if(requiredType.isArray()){
                    return (T) convertToTypedArray(convertedValue, requiredType.getComponentType());
                }

                //如果待设置的值是一个数组，且只有一个元素，则转成普通对象
                if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) {
                    convertedValue = Array.get(convertedValue, 0);

                    //再次进行类型转换
                    if (!ClassUtils.isAssignableValue(requiredType, convertedValue)) {
                        TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
                        if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
                            convertedValue =  conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
                        }
                    }
                }
            }
            return (T) convertedValue;
        }
        catch (ConverterNotFoundException ex) {
            throw new ConversionNotSupportedException(newValue, requiredType, ex);
        }
        catch (ConversionException ex) {
            throw new TypeMismatchException(newValue, requiredType, ex);
        }
    }


    private Object convertToTypedArray(Object input, Class<?> componentType) {
        if (input.getClass().isArray()) {
            int arrayLength = Array.getLength(input);
            Object result = Array.newInstance(componentType, arrayLength);

            //遍历数组，对每个元素进行转换
            for (int i = 0; i < arrayLength; i++) {
                Object value = convertIfNecessary(Array.get(input, i), componentType);
                Array.set(result, i, value);
            }
            return result;
        }
        return null;
    }


    public void setConversionService(ConversionService conversionService) {
        this.conversionService = conversionService;
    }

    public ConversionService getConversionService() {
        return conversionService;
    }
}
